home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Developer Helper 1: Phil & Dave's Excellent CD
/
Excellent CD HFS.raw
/
Moof
/
Goodies
/
HyperCard Goodies
/
HyperCard Dev. ToolKit
/
Serial & MacinTalk XCMDs
/
XCMD.HowToUse.doc
< prev
next >
Wrap
Text File
|
1987-07-14
|
7KB
|
179 lines
XCMD and XFCN: The Magic Hooks That Extend HyperTalk
(This documentation by Ted Kaehler, 14 July 87, HyperCard 1.0B9 and later)
Dan Winkler has created an interface that allows powerful new commands
to be added to HyperCard "in the field." When a command in a script
cannot be found, HyperCard looks for a resource of type XCMD with
the same name as the unknown command. Likewise, when a function cannot
be found, HyperCard looks for a resource of type XFCN. Consider XCMDs
and XFCNs to be extensions of stack scripts. Whenever a handler (for a
command or function) is not found in a stack script, HyperCard
immediately looks for a XCMD or XFCN in that stack. The total
inheritance order is: Button (or Field), Card, Stack, stack XCMD, Home,
home XCMD, HyperCard. An XCMD or XFCN is a code resource with no
header bytes (just like a desk accessory). You can move them from
file to file with ResEdit or with any other resource moving tool.
The only thing passed into an XCMD or XFCN is a pointer to a XCmdBlock.
It looks like this:
XCmdPtr = ^XCmdBlock;
XCmdBlock =
RECORD
paramCount: INTEGER; { number of arguments }
params: ARRAY[1..32] OF Handle; { the arguments }
returnValue: Handle; { the result of this XCMD }
passFlag: BOOLEAN; { pass the message on? }
entryPoint: ProcPtr; { call back to HyperCard }
request: INTEGER; { what you want }
result: INTEGER; { the answer it gives }
inArgs: ARRAY[1..16] OF LongInt; { args XCMD sends HyperCard }
outArgs: ARRAY[1..16] OF LongInt; { args HyperCard sends back }
END;
You read the agruments (they are handles to zero terminated strings),
do whatever the purpose of this XCMD is, and optionally store a
result into returnValue. All data values going to and from HyperTalk are zero-terminated ASCII
strings.
Resources of type XCMD are commands, and resourcs of type XFCN are
fuctions that return a value. If you store a result string into
returnValue in a command, the user can get it by asking for "the result"
(useful for explaining why there was an error). In a function, you are
expected to store the answer into returnValue.
If passFlag is false (the normal case), this XCMD or XFCN has handled the
message and the script resumes execution. If passFlag is true, HyperCard
searches the remaining inheritance chain for another handler or
XCMD with the same name. This is just like the "pass" control structure
in a script.
The file Flash.p is an example XCMD. It takes one argument which is
the ASCII characters for a decimal integer. It checks if the number is
even and inverts the screen that number of times. It is meant to be used
as a command and puts an error string into "the result" if the argument is
odd.
Peek.p is a function (XFCN) that returns the value of any memory location
in the machine (purists avert your eyes).
The second part of the XCmdBlock record has to do with
calling HyperCard back in the middle of your code to ask a question.
If you wanted manage the call to HyperCard yourself, you would
fill inArgs with your arguments, put a request code in request,
and JSR to the address in entryPoint. HyperCard returns the values you
requested in outArgs and a reslut code in result.
However, Dan Winkler has packaged the entire range of calls on HyperCard,
so that if you are using Pascal, you can simply call a procedure. Both
Peek and Flash use some conversion routines that Dan has kindly
supplied. The file XCmdGlue.inc has the glue procedures. Handle is always
a handle to a zero-terminated sting. If a handle is returned, you are
responsible for freeing it. <<is this true?>>
FUNCTION EvalExpr(expr: Str255): Handle;
Given a HyperTalk expression in ASCII, return a handle to a zero-terminated
string with the answer.
PROCEDURE SendCardMessage(msg: Str255);
Send a HyperCard message (command) to the current card.
FUNCTION GetGlobal(globName: Str255): Handle;
PROCEDURE SetGlobal(globName: Str255; globValue: Handle);
FUNCTION GetFieldByName(cardFieldFlag: BOOLEAN; fieldName: Str255): Handle;
FUNCTION GetFieldByNum(cardFieldFlag: BOOLEAN; fieldNum: INTEGER): Handle;
FUNCTION GetFieldByID(cardFieldFlag: BOOLEAN; fieldID: INTEGER): Handle;
PROCEDURE SetFieldByName(cardFieldFlag: BOOLEAN; fieldName: Str255; fieldVal: Handle);
PROCEDURE SetFieldByNum(cardFieldFlag: BOOLEAN; fieldNum: INTEGER; fieldVal: Handle);
PROCEDURE SetFieldByID(cardFieldFlag: BOOLEAN; fieldID: INTEGER; fieldVal: Handle);
PROCEDURE ZeroToPas(zeroStr: Ptr; VAR pasStr: Str255);
Convert a zero terminated string to a Pascal string. You create the
Pascal string and pass it in as a VAR parameter. Useful for converting
the arguments of any XCMD to Pascal strings. (See PasToZero below).
FUNCTION StrToNum(str: Str19): LongInt;
Convert ASCII of a decimal number to the number.
FUNCTION StrToLong(str: Str19): LongInt;
Convert ASCII of a decimal number to the number. Negative numbers not
accepted.
FUNCTION StrToExt(str: Str31): Extended;
FUNCTION StrToBool(str: Str31): BOOLEAN;
Convert ASCII "true" and "false" to Pascal booleans.
FUNCTION StringLength(strPtr: Ptr): LongInt;
FUNCTION StringEqual(str1,str2: Str255): BOOLEAN;
FUNCTION StringMatch(pattern: Str255; target: Ptr): Ptr;
PROCEDURE ReturnToPas(zeroStr: Ptr; VAR pasStr: Str255);
PROCEDURE ScanToReturn(VAR scanPtr: Ptr);
PROCEDURE ScanToZero(VAR scanPtr: Ptr);
PROCEDURE ZeroBytes(dstPtr: Ptr; longCount: LongInt);
PROCEDURE CopyBytes(srcPtr,dstPtr: Ptr; byteCount: LongInt);
Generally useful for moving data in memory.
PROCEDURE DivMod(numerator: LongInt; denominator: INTEGER;
VAR quotient: LongInt; VAR remainder: INTEGER);
Division with remainder.
FUNCTION LongToStr(posNum: LongInt): Str19;
Convert a longInt to decimal ASCII.
FUNCTION NumToStr(num: LongInt): Str19;
Convert a longInt to decimal ASCII, using a minus sign for negative
numbers.
FUNCTION NumToHex(num: LongInt; nDigits: INTEGER): Str19;
Convert a longInt to hexadecimal ASCII, using a minus sign for
negative numbers.
FUNCTION ExtToStr(num: Extended): Str31;
FUNCTION BoolToStr(bool: BOOLEAN): Str31;
Convert a boolean to ASCII "true" or "false".
FUNCTION PasToZero(str: Str255): Handle;
Convert a pascal string to a zero-terminated string. You'll need to do
this for any result or argument you return to HyperTalk.
Here are the files you will need:
HyperXCmd.p
XCmdGlue.inc
Flash.p (Example to see how everything is really done. A command.)
Peek.p (Example to see how everything is really done. A function.)
Here are the typical MPW commands for compiling an XCMD
pascal -w SendSerial.p
link -m ENTRYPOINT -o HyperCommands -rt XCMD=222 -sn Main=SendSerial ∂
SendSerial.p.o "{MPW}"Libraries:interface.o
If you don't use any of the routines in interface.o, its just
pascal Flash.p
link -o HyperCommands -rt XCMD=0 -sn Main=Flash Flash.p.o
After executing these, use ResEdit to move the XCMD or XFCN from
HyperCommands to the proper stack.